package com.raymond.http.netty;

import com.raymond.http.config.HttpConfig;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.pool.ChannelPoolMap;
import io.netty.channel.pool.FixedChannelPool;
import io.netty.channel.pool.SimpleChannelPool;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;

import java.net.URI;
import java.util.concurrent.*;

/**
 * http-netty连接池
 *
 * @author :  raymond
 * @version :  V1.0
 * @date :  2021-12-11 15:02
 */
@Slf4j
public class HttpNetty {


    public static HttpNetty httpNetty;


    /**
     * 是否是linux系统
     */
    protected boolean isLinux = System.getProperty("os.name").toLowerCase().contains("linux");
    protected Bootstrap bootstrap = new Bootstrap();
    protected EventLoopGroup eventLoopGroup = isLinux ? new EpollEventLoopGroup() : new NioEventLoopGroup();
    /**
     * 是否初始化
     */
    protected volatile boolean isInit = Boolean.FALSE;

    protected HttpConfig config;

    /**
     * 通道池，这里面存放了所有连接池的map映射
     */
    protected DefaultChannelPoolMap poolMap;


    private HandlerInstance instance;

    public HttpNetty() {
        this(new HttpConfig());
    }

    public HttpNetty(HttpConfig config) {
        this(config, (fixedChannelPool) -> {
            log.debug("fixedChannelPool:{}", fixedChannelPool);
            return new DefaultHttpClientHandler(fixedChannelPool);
        });
    }

    public HttpNetty(HttpConfig config, HandlerInstance instance) {
        this.config = config;
        this.instance = instance;
    }

    public synchronized boolean init() {
        if (isLinux) {
            //ePoll轮询空转BUG
            System.setProperty("org.jboss.netty.epollBugWorkaround", "true");
            bootstrap.channel(EpollServerSocketChannel.class);
        } else {
            bootstrap.channel(NioSocketChannel.class);
        }
        bootstrap.group(eventLoopGroup)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeOutMillis())
                .handler(new LoggingHandler(config.getLogLevel()))
                .option(ChannelOption.TCP_NODELAY, config.getTcpNodeLazy())
                .option(ChannelOption.SO_KEEPALIVE, config.getSoKeepAlive());
        poolMap = new DefaultChannelPoolMap(bootstrap, config, instance);
        isInit = Boolean.TRUE;
        HttpNetty.httpNetty = this;
        return true;
    }

    public HttpFixedChannelPool getFixedChannelPool(String url) {
        return poolMap.get(new HttpAddress(url));
    }

    public HttpFixedChannelPool getFixedChannelPool(HttpAddress address) {
        return poolMap.get(address);
    }

    public HttpFixedChannelPool getFixedChannelPool(URI uri) {
        return poolMap.get(new HttpAddress(uri));
    }

    public Channel getChannel(URI uri) {
        return getChannel(getFixedChannelPool(uri));
    }

    public Channel getChannel(String url) {
        return getChannel(getFixedChannelPool(url));
    }

    public Channel getChannel(HttpFixedChannelPool fixedChannelPool) {
        return fixedChannelPool.acquire();
//            return future.get(config.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS);
    }

    public ChannelFuture request(Channel channel, HttpRequest httpRequest) {
        return channel.writeAndFlush(httpRequest);
    }

    public HttpConfig getConfig() {
        return config;
    }

}
